/*
 * @file: 接口请求共用模块, 包含: 请求重试, 请求取消, 请求/响应拦截, 限制同一时间请求个数等功能
 */
// import Mock from 'mockjs'
// import mockData from '../../mock/index.js'

// const requestNum = parseInt(process.env.VUE_APP_REQUEST_NUM)
// console.log(requestNum)
// 同时间请求接口的数量
let requestNum = 10;
// 超时时间
let timeout = 200;
// 同时间请求接口的数量
let retry = 3;

// 请求队列, 用于限制同一时间请求的个数
class RequestList {
  constructor() {
    this.requestList = [] // 请求队列(只记录url)
    this.waitList = [] // 等待队列
  }

  // 进入请求队列
  pushRequest(url) {
    if (this.requestList.indexOf(url) === -1) {
      this.requestList.push(url)
    }
  }

  // 进入等待队列
  pushWait(url, options, cb) {
    let isExit = false
    this.waitList.some((item) => {
      if (item.url === url) {
        isExit = true
        return isExit
      }
      return false
    })
    if (!isExit) {
      this.waitList.push({
        url,
        options,
        cb
      })
    }
  }

  // 请求下一个(当前请求出队并请求等待队列中的第一个等待)
  requestNext(url) {
    if (this.requestList.indexOf(url) !== -1) {
      this.requestList.splice(this.requestList.indexOf(url), 1)
    }
    if (this.waitList.length > 0) {
      const next = this.waitList.shift()
      next.cb && next.cb(request(next.options))
    }
  }
}
const requestList = new RequestList()

// 接口请求取消
class CancelController {
  constructor() {
    this.requestTaskMap = {}
  }

  // 缓存上一次的请求
  push(uniqueKey, requestTask) {
    if (!this.requestTaskMap[uniqueKey] || this.requestTaskMap[uniqueKey] === 'cancel') {
      this.requestTaskMap[uniqueKey] = []
    }
    this.requestTaskMap[uniqueKey].push(requestTask)
  }

  // 终止请求
  cancel(uniqueKey) {
    if (!this.requestTaskMap[uniqueKey] || this.requestTaskMap[uniqueKey] === 'cancel') return true
    this.requestTaskMap[uniqueKey].forEach(requestTask => {
      requestTask.abort()
    })
    this.requestTaskMap[uniqueKey] = 'cancel'
  }

  start(url, requestTask) {
    const uniqueKey = url
    this.cancel(uniqueKey)
    this.push(uniqueKey, requestTask)
    return requestTask
  }
}

const cancelController = new CancelController()

// 接口重试
class RetryController {
  constructor(request, options) {
    if (typeof request !== 'function') throw new Error('request参数仅支持函数类型')
    if (!options) throw new Error('options参数不允许为空')
    this.request = request
    this.options = options
  }

  async tryRequest() {
    try {
      return await this.request(this.options)
    } catch (e) {
      return this.start(e)
    }
  }

  // 网络请求超时，发起重试
  async start(response) {
    if (cancelController.requestTaskMap[this.options.url] === 'cancel') {
      // 接口被取消
      throw response
    }
    if (response.errMsg !== 'request:fail timeout') throw response

    if (!this.options.config) {
      this.options.config = {
        retry: parseInt(retry)
      }
    }
    if (this.options.config.retry === undefined) {
      this.options.config.retry = parseInt(retry)
    }
    if (this.options.config.retry > 0) {
      this.options.config.retry--
      return this.tryRequest()
    }
    throw response
  }
}

const showLoading = (ifHideLoading) => {
  !ifHideLoading && uni.showLoading({
    title: '加载中'
  })
}

const hideLoading = () => {
  uni.hideLoading()
}

/**
 * @description: 接口请求
 * @param {String} url 请求地址
 * @param {Object} data 请求参数
 * @param {String} method 请求方式, 默认是 GET, 有效值: GET POST PUT DELETE
 * @param {Object} header 请求头
 * @param {Number} timeout 超时时间,单位ms.
 * @param {String} dataType 数据类型, 默认是 json
 * @param {String} responseType 设置响应的数据类型, 默认是 text, 合法值：text、arraybuffer
 * @param {Boolean} cancel 是否取消上一次未完成请求，默认值true
 * @param {Boolean} isNotJoinDomain 是否不需要拼接域名, 默认值false。 true:不拼接域名, false:拼接域名
 * @return {Promise} Promise对象
 */
function request(options) {
  let { url, data, method, header, timeout, dataType, responseType, cancel = true, isNotJoinDomain = false } = options
  if (!url) {
    throw new Error('url不允许为空')
  }
  // TODO 小程序登录状态校验.
  timeout = timeout || parseInt(timeout)
  dataType = dataType || 'json'
  responseType = responseType || 'text'
  method = method || 'GET'
  header = header || { 'Content-Type': 'application/json;charset=utf-8;' }
  // #ifdef MP-WEIXIN
  // 小程序端、APP端的mock数据
  // if (process.env.VUE_APP_MOCK === '1') {
  //   return new Promise((resolve, reject) => {
  //     if (mockData[url]) {
  //       resolve(Mock.mock({
  //         code: 200,
  //         message: '成功',
  //         success: true,
  //         object: mockData[url]
  //       }))
  //     } else {
  //       reject(new Error(`invalid url "${url}"`))
  //     }
  //   })
  // }
  // #endif
  const originUrl = url
  // if (!url.startsWith('http') && !url.startsWith('//') && !isNotJoinDomain) {
  //   url = `${process.env.VUE_APP_DOMAIN}${url}`
  // }
  if (requestList.requestList.length < requestNum || requestList.requestList.indexOf(originUrl) !== -1) {
    requestList.pushRequest(originUrl)
    return new Promise((resolve, reject) => {
      const requestTask = uni.request({
        url,
        data,
        method,
        header,
        timeout,
        dataType,
        responseType,
        success({ data }) {
          if (!data || data.code !== 200) {
            return reject(data)
          }
          resolve(data)
        },
        fail(res) {
          console.info('fail ', res)
          reject(res)
        }
      })
      // 多次请求同一个接口取消上一次未完成请求
      if (cancel) {
        cancelController.start(originUrl, requestTask)
      }
    })
  } else {
	  console.log(222)
    return new Promise((resolve) => {
      requestList.pushWait(originUrl, options, (res) => {
        resolve(res)
      })
    })
  }
}

export async function post(options) {
  showLoading(options.ifHideLoading)
  options.method = 'POST'
  let res = null
  try {
    res = await request(options)
  } catch (e) {
    try {
      const retryController = new RetryController(request, options)
      res = await retryController.start(e)
    } catch {}
  }
  hideLoading()
  requestList.requestNext(options.url)
  return res

}

export async function get(options) {
  showLoading(options.ifHideLoading)
  let res = null
  try {
    res = await request(options)
  } catch (err) {
    try {
      // 重试
      const retryController = new RetryController(request, options)
      res = await retryController.start(err)
    } catch {}
  }
  hideLoading()
  requestList.requestNext(options.url)
  return res
}

/**
 * 终止请求
 * @param {*} url 需要终止请求的接口地址
 */
export function cancel(url) {
  cancelController.cancel(url)
  requestList.requestNext(url)
  hideLoading()
}

// TODO 上传

// TODO 下载

export default {
  post,
  get,
  cancel
}
